﻿using System;
using System.Windows.Media.Media3D;
using System.Windows;
using Petzold.Media3D;
using System.Windows.Input;
using System.Windows.Controls;
using System.Windows.Media.Animation;

namespace Wpf3dCollaborative
{

    /// <summary>
    ///   Cette classe gére toute les transformations effective d'un objet 3D
    /// </summary>
    public class Transformer3D : IDisposable
    {

        /* Positions/échelles Courantes */
        public TranslateTransform3D tr = new TranslateTransform3D();
        public ScaleTransform3D sc = new ScaleTransform3D();
        public RotateTransform3D rotation = new RotateTransform3D();
        public Transform3DGroup GlobalTransform = new Transform3DGroup();
        public AxisAngleRotation3D rotateAxis =new AxisAngleRotation3D();

        /* Sauvegarde des anciennes positions/échelles */
        public Point3D LastPosition3D = new Point3D();
        public Point3D LastPosition3DDeplacement = new Point3D(); // position spécifique au déplacement
        public Point3D LastScale3D = new Point3D();

        private bool disposed = false; 
        /* Statut de déplacement */
        public Point3D pointOriginal;// Point d'origine du mouvement ( drag move)
        public bool IsTracking = false; // Déplacement souris

        public Transformer3D()
        {

          
            GlobalTransform.Children.Add(tr);
            GlobalTransform.Children.Add(sc);
            GlobalTransform.Children.Add(rotation);

        }

             #region PositionMethods
        public void Deplacer(double x, double y, double z)
        {
            // TODO attention Z
            tr.OffsetX += x;
            tr.OffsetY += y;
            tr.OffsetZ = z;
        }



        public void Redimensionner(double x, double y, double z, bool isAditive)
        {

            SaveLastScale();
            if (isAditive)
            {
                sc.ScaleX += x;
                sc.ScaleY += y;
                sc.ScaleZ += z;
            }
            else
            {
                sc.ScaleX = x;
                sc.ScaleY = y;
                sc.ScaleZ = z;
            }

        }

        /// <summary>
        ///  Permet de sauvegarder la position de l'objet dans l'espace
        /// </summary>
        public void SaveLastPosition()
        {

            LastPosition3D.X = tr.OffsetX;
            LastPosition3D.Y = tr.OffsetY;
            LastPosition3D.Z = tr.OffsetZ;
        }

        public void SaveLastScale()
        {
            LastScale3D.X = sc.ScaleX;
            LastScale3D.Y = sc.ScaleY;
            LastScale3D.Z = sc.ScaleZ;
        }

        // sauvegarde toutes les positions de l'objet
        public void SaveLastLocalisation()
        {
            SaveLastScale();
            SaveLastPosition();
        }




        /* Permet le calcul d'une transformation d'angle pour un carroussel */
        public void TranformCaroussel(int index, double angle, double radius, int numberElement)
        {
            tr.OffsetX = 0;
            tr.OffsetY = 0;
            tr.OffsetZ = (double)(radius - numberElement / 2.0);

             

            rotateAxis = new AxisAngleRotation3D(new Vector3D(0, 1, 0), index * angle);


          
            rotation.Rotation = rotateAxis;

            SaveLastPosition();
        }

        /* Permet le calcul d'une transformation d'angle pour un carroussel */
        public void TranformCaroussel2(int index, double angle, double radius, int numberElement)
        {
            tr.OffsetX = 0;
            tr.OffsetY = 0;
            tr.OffsetZ = (double)(radius - numberElement / 2.0);



            rotateAxis = new AxisAngleRotation3D(new Vector3D(0, 1, 1), index * angle);



            rotation.Rotation = rotateAxis;

            SaveLastPosition();
        }



        public void SetWideScreen( ref Boolean IsWide)
        {
            if (IsWide)
            {
                IsWide = false;
                sc.ScaleX = sc.ScaleX / 1.6;
                sc.CenterX = tr.OffsetX;
            }
            else
            {

                IsWide = true;
                sc.ScaleX = sc.ScaleX * 1.6;
                sc.CenterX = tr.OffsetX;
            }

        }

        /* Gestion du déplacement souris */
        void AdjustTranslateTransform(Point3D pointNew, bool IsFullScreen)
        {

            if (!IsFullScreen)
            {
                Vector3D vectMouse = pointNew - pointOriginal;
                tr.OffsetX = LastPosition3D.X + vectMouse.X;
                tr.OffsetY = LastPosition3D.Y + vectMouse.Y;
                tr.OffsetZ = LastPosition3D.Z + vectMouse.Z;

            }
            else
            {
                Vector3D vectMouse = pointNew - pointOriginal;
                tr.OffsetX = LastPosition3DDeplacement.X + vectMouse.X;
                tr.OffsetY = LastPosition3DDeplacement.Y + vectMouse.Y;
                tr.OffsetZ = LastPosition3DDeplacement.Z + vectMouse.Z;
            }

        }
        #endregion PositionMethods

        public void StartDrag(MouseButtonEventArgs e, bool IsFullScreen, Viewport3D vis)
        {

            Point ptMouse = e.GetPosition(App.windows.mainViewport);
            LineRange range;
            ViewportInfo.Point2DtoPoint3D(vis, ptMouse, out range);
            pointOriginal = range.PointFromZ(tr.OffsetZ);
            if (!IsFullScreen)
            {
                LastPosition3D.X = tr.OffsetX;
                LastPosition3D.Y = tr.OffsetY;
                LastPosition3D.Z = tr.OffsetZ;
            }
            else
            {
                LastPosition3DDeplacement.X = tr.OffsetX;
                LastPosition3DDeplacement.Y = tr.OffsetY;
                LastPosition3DDeplacement.Z = tr.OffsetZ;
            }
            IsTracking = true;

        }

        public void DragMove(MouseEventArgs e, bool IsFullScreen, Viewport3D Repere)
        {
            if (!IsTracking)
                return;

            // On récupére la position de la souris et on calcul la transformation
            Point ptMouse = e.GetPosition(Repere);
            LineRange range;
            ViewportInfo.Point2DtoPoint3D(Repere, ptMouse, out range);
            Point3D pointNew = range.PointFromZ(tr.OffsetZ);
            AdjustTranslateTransform(pointNew, IsFullScreen);
        }

        public void MouseWheel(MouseWheelEventArgs e, bool IsFullScreen, Viewport3D Repere)
        {
            // On récupére la position de la souris pour gérer le déplacement en Z
            Point ptMouse = e.GetPosition(Repere);
            LineRange range;
            ViewportInfo.Point2DtoPoint3D(Repere, ptMouse, out range);
            Point3D pointNew = range.PointFromZ(tr.OffsetZ);
            pointNew.Z += e.Delta / 100;


            AdjustTranslateTransform(pointNew, IsFullScreen);

        }

        public void LostMouseCapture()
        {
            // Si le déplacement est annulé on restaure les valeur
            if (IsTracking)
            {
                tr.OffsetX = LastPosition3D.X;
                tr.OffsetY = LastPosition3D.Y;
                tr.OffsetZ = LastPosition3D.Z;
                IsTracking = false;
            }
        }

        /* Méthode utile aux objets se comportants comme des caméra */
        public void FullScreen(Boolean IsFullScreen, bool IsWideCam)
        {
            // TODO gérer le cas du caroussel 
            // ClearTranslateTransform();
            tr.BeginAnimation(TranslateTransform3D.OffsetZProperty,
   null);
            DoubleAnimation Anim = new DoubleAnimation();
            Anim.Duration = new Duration(TimeSpan.FromMilliseconds(500));

            double PasTranformation = 3.95;
            if (tr.OffsetZ < 100)
            {
                SaveLastLocalisation();

                Anim.From = tr.OffsetZ;
                Anim.To = 100;

                IsFullScreen = true;

                if (IsWideCam)
                {
                    tr.OffsetX = -4;
                    tr.OffsetY = 0;
                    tr.OffsetZ = 100;
                    sc.CenterX = tr.OffsetX;
                    SaveLastScale();
                    Redimensionner(PasTranformation + (0.6 * PasTranformation), PasTranformation, 0.0, true);

                }
                else
                {
                    tr.OffsetX = -2.5;
                    tr.OffsetY = 0;
                    tr.OffsetZ = 100;

                    sc.CenterX = tr.OffsetX;
                    SaveLastScale();
                    Redimensionner(PasTranformation + 0.6, PasTranformation, 0.0, true);

                }



            }
            else
            {

                sc.ScaleX = LastScale3D.X;
                sc.ScaleY = LastScale3D.Y;
                sc.ScaleZ = LastScale3D.Z;
                Anim.From = tr.OffsetZ;
                Anim.To = 0;

                tr.OffsetY = LastPosition3D.Y;
                tr.OffsetX = LastPosition3D.X;
                tr.OffsetZ = 0;
                IsFullScreen = false;

                sc.CenterX = tr.OffsetX;

                SaveLastLocalisation();
            }

            tr.BeginAnimation(TranslateTransform3D.OffsetZProperty,
Anim);

        }




        /*
         * ANIMATIONS
         * 
         */
        public void AnnimCaroussel(int duree)
        {
            rotation.Rotation = new AxisAngleRotation3D(new Vector3D(0, 1, 0), 0);
            rotation.Rotation.BeginAnimation(AxisAngleRotation3D.AngleProperty,
                null);

            rotation.CenterZ = tr.OffsetZ;

            DoubleAnimation AnimFull = new DoubleAnimation();
            AnimFull.Duration = TimeSpan.FromMilliseconds(duree);
            AnimFull.RepeatBehavior = RepeatBehavior.Forever;
            AnimFull.From = 0;
            AnimFull.To = 360;
            rotation.Rotation.BeginAnimation(AxisAngleRotation3D.AngleProperty,
              AnimFull);

        }

        /*
        * ANIMATIONS
        * 
        */
        public void StopAnim()
        {
            rotation.Rotation = new AxisAngleRotation3D(new Vector3D(0, 1, 0), 0);
            rotation.Rotation.BeginAnimation(AxisAngleRotation3D.AngleProperty,
                null);

        }



        public void SoucoupeVolante(int duree)
        {
            rotation.Rotation = new AxisAngleRotation3D(new Vector3D(0, 90, 90), 180);
            rotation.Rotation.BeginAnimation(AxisAngleRotation3D.AngleProperty,
             null);

            rotation.CenterZ = 0;

            DoubleAnimation AnimFull = new DoubleAnimation();
            AnimFull.Duration = TimeSpan.FromMilliseconds(duree);
            AnimFull.RepeatBehavior = RepeatBehavior.Forever;

            AnimFull.From = 0;
            AnimFull.To = 360;
            rotation.Rotation.BeginAnimation(AxisAngleRotation3D.AngleProperty,
              AnimFull);

        }


        /*
         * 
         * METHODES D'EFFACEMENT / INITIALISATION
         * 
         */
             #region Clean
        public void ClearTranslateTransform()
        {

            tr.OffsetX = 0;
            tr.OffsetY = 0;
            tr.OffsetZ = 0;

        }



        public void ClearScaleTransform()
        {
            sc.ScaleX = 1.0;
            sc.ScaleY = 1.0;
            sc.ScaleZ = 1.0;

        }

        public void ClearRotateTransform()
        {
            rotation.CenterX = 0;
            rotation.CenterY = 0;
            rotation.CenterZ = 0;
            if (rotateAxis != null)
            {
                rotateAxis.Angle = 0;
            }
        }


        public void ClearTransforms()
        {
            ClearRotateTransform();
            ClearScaleTransform();
            ClearTranslateTransform();
            //Dispose();

            tr = new TranslateTransform3D();
            sc = new ScaleTransform3D();
            rotation = new RotateTransform3D();
            GlobalTransform = new Transform3DGroup();


            /* Sauvegarde des anciennes positions/échelles */
            LastPosition3D = new Point3D();
            LastPosition3DDeplacement = new Point3D(); // position spécifique au déplacement
            LastScale3D = new Point3D();
            GlobalTransform.Children.Add(tr);
            GlobalTransform.Children.Add(sc);
            GlobalTransform.Children.Add(rotation);

            sc.CenterX = 0;
            sc.CenterY = 0;
            sc.CenterZ = 0;

        }


        private void Dispose(bool disposing)
        {
        
            if (!this.disposed)
            {
           
                if (disposing)
                {
                    Console.WriteLine("[DESTRUCTION Transforms]");
                    GlobalTransform = null;
                    tr = null;
                    sc = null;
                    rotateAxis = null;

                    rotation = null;

                    GlobalTransform = null;

                }



            }
            disposed = true;
        }

        public void Dispose()
        {
            Dispose(true);

            GC.SuppressFinalize(this);
        }


        ~Transformer3D()
        {

            Dispose(false);

        }
             #endregion Clean
    }
}
